package
{
	import flash.display.Sprite;
	import flash.events.TimerEvent;
	import flash.geom.Point;
	import flash.utils.Timer;

	public class PathFinder
	{
		public static const ENABLE_VISIUALIZATION:Boolean = false;
		
		public var display:Sprite;
		
		private var m_world:World;
		private var m_finalDestination:Point;
		private var m_waypointQueue:Vector.<Point>;
		private var m_fromPosition:Point;
		private var m_visionRadius:Number;
		private var m_visualization:Sprite;
		private var m_ready:Boolean = true;
		private var m_pastBestNodes:AstarList; // used for when agents can't find their way out
		private var m_ticker:Timer = new Timer(1000/10);
		
		
		public function PathFinder(world:World)
		{
			m_world = world;
			m_waypointQueue = new Vector.<Point>;
			m_pastBestNodes = new AstarList();
			if(ENABLE_VISIUALIZATION){
				display = new Sprite();
			}	
			
			m_ticker.addEventListener(TimerEvent.TIMER, function(te:TimerEvent):void{
				if(m_ready == false){
					m_ready = true;
				}
			});
			m_ticker.start();
		}
		
		public function isReady():Boolean{
			return m_ready;
		}
		
		public function initialize():void{
			
		}
		
		public function setup(finalDestination:Point,visionRadius:Number=10):void{
			m_finalDestination = finalDestination;	
			m_visionRadius = visionRadius;
		}
		
		public function getNextWaypoint(fromPosition:Point):Point{
			m_ready = false;
			m_fromPosition = Util.clampPoint(fromPosition, 1);
			
			if(m_waypointQueue.length == 0){
				doPathfinding();
			}
			
			return m_waypointQueue.pop();
		}
		
		private function doAstar(from:AstarNode, to:AstarNode, openList:AstarList, closedList:AstarList):void{
			var bestNode:AstarNode;
			
			if(!openList.isEmpty()){
				if(from.location.subtract(to.location).length > 0){
					closedList.add(from);
					openList.remove(from);
							
					for each(var adjNode:AstarNode in getAdjacentNodes(from, to)){
						adjNode.parent = from;
						if(!openList.contains(adjNode) && !closedList.contains(adjNode)){
							if(m_pastBestNodes.contains(adjNode)){
								adjNode.g += adjNode.g;
							}
							openList.add(adjNode);	
						}
					}
					
					bestNode = openList.getBestNode();
					if(bestNode){
						doAstar(bestNode, to, openList, closedList);
					}
				}
			}
		}
		
		private function getAdjacentNodes(from:AstarNode, to:AstarNode):Vector.<AstarNode>{
			var i:int, j:int;
			var node:AstarNode;
			var nodes:Vector.<AstarNode> = new Vector.<AstarNode>;
			var pos:Point;
			
			for(i = -1; i < 2; i++){
				for(j = -1; j < 2; j++){
					if(i != j && i != -j){ // Don't allow diagonal movement
						pos = new Point(i + from.location.x, j + from.location.y);
						if(Global.WORLD_RECT.containsPoint(pos)){
							var multiplier:int = 1;//Global.PRNG.nextIntRange(1,5);
							if(pos.subtract(m_fromPosition).length <= m_visionRadius*multiplier){
								var collisionResults:Vector.<ISpatialIndexable> = m_world.getCollisionsByPoint(pos);
								if(collisionResults.length == 0){
									var weight:Number = .25;
									node = new AstarNode();
									node.location = pos;
									node.g = m_finalDestination.subtract(pos).length;
									node.f = from.f + pos.subtract(from.location).length * weight;
									nodes.push(node);
								}
							}
						}
					}
				}
			}
			
			return nodes;
		}
		
		private function doPathfinding():void{
			var from:AstarNode = new AstarNode();
			from.location = m_fromPosition;
			from.f = 1;
			from.g = m_finalDestination.subtract(m_fromPosition).length;
			
			var to:AstarNode = new AstarNode();
			to.location = m_finalDestination;
			to.f = m_finalDestination.subtract(m_fromPosition).length;
			to.g = 0;			
			
			var openList:AstarList = new AstarList();
			if(m_pastBestNodes.contains(from)){
				from.g += from.g;
			}
			openList.add(from);

			var closedList:AstarList = new AstarList();			
			doAstar(from, to, openList, closedList);
			
			var bestNode:AstarNode = closedList.getBestNode();
			
			m_pastBestNodes.add(bestNode);
//			if(m_pastBestNodes.nodeList.length > 20){
//				m_pastBestNodes.nodeList.shift();
//			}
			
			var node:AstarNode = bestNode;
			m_waypointQueue.push(bestNode.location);
			while(node.parent != null){
				node = node.parent;
				m_waypointQueue.push(node.location);
			}
			
			//m_waypointQueue.push(m_finalDestination);
			if(ENABLE_VISIUALIZATION){
				var i:int, il:int;
				for(i = 0, il = m_world.pathingDebugDisplay.numChildren; i < il; i++){
					m_world.pathingDebugDisplay.removeChildAt(0);
				}
				
				for each(node in closedList.nodeList.getAllNodes()){
					m_world.pathingDebugDisplay.addChild(node.getDebugImage(0xffff55));
				}
				m_world.pathingDebugDisplay.addChild(bestNode.getDebugImage(0x00ff00));				
				display.x = m_fromPosition.x * Global.PPU;
				display.y = m_fromPosition.y * Global.PPU;
			}			
		}
	}
}